/**
 * file :	db_interface.cpp
 * author :	bushaofeng
 * created :	2013-01-29 15:15
 * func : 
 */

#include <time.h>
#include <set>
#include "wa_define.h"
#include "wa_exception.h"
#include "db_interface.h"

DBInterface::DBInterface(MongoClient& db, string token):m_db(db){
    m_db.ensureIndex(DB_PROJECTS, BSON("project"<<1), true, "projects_name");
    BSONObj obj = m_db.findOne(DB_PROJECTS, BSON("_id"<<token));
    if(obj.nFields()>0){
        m_project = obj.getField("project").String();
        m_users     = m_project + ".users";
        m_follows   = m_project + ".follows";
        m_fans      = m_project + ".fans";
        m_topics    = m_project + ".topics";
        m_posts     = m_project + ".posts";
        m_locations = m_project + ".locations";
        m_inbox     = m_project + ".inbox";
    }
    else{
        throw WAException(WA_ERR_PROJ_NOTFOUND);
    }

}

string DBInterface::createProject(string project){
        // 后续对project token做处理，时间+project转md5
    string token = project;
    m_db.insert(DB_PROJECTS, BSON("_id"<<token<<"project"<<project<<"_tm"<<(unsigned int)time(0)));
    if(m_db.getLastError()!=""){
        throw WAException(WA_ERR_PROJ, m_db.getLastErrorDetailed().getIntField("code"));
    }
    m_db.ensureIndex(DB_PROJECTS, BSON("project"<<1), true, "projects_name");

    m_project   = project;
    m_users     = m_project + ".users";
    m_follows   = m_project + ".follows";
    m_fans      = m_project + ".fans";
    m_topics    = m_project + ".topics";
    m_posts     = m_project + ".posts";
    m_locations = m_project + ".locations";
    m_inbox     = m_project + ".inbox";

    setCounter(m_follows);
    setCounter(m_users);
    setCounter(m_fans);
    setCounter(m_topics);
    setCounter(m_posts);
    setCounter(m_inbox);
    setCounter(m_locations);
    setIndexs();

    return token;
}

/**
 * 添加一个user
 */
long long DBInterface::addUser(BSONObj user){
    BSONObjBuilder tbuilder;
    long long      id = -1;
    
    if(m_db.findOne(m_users, BSON("user"<<user.getStringField("user"))).nFields()>0){
        throw WAException(WA_ERR_USER_DUP);
    }

    try{
        id = getCounterId(m_users);
        if(id < 0){
            throw string("Get User Id Error");
        }
    }catch(string& e){
        throw m_users+":"+e;
    }

    tbuilder.append("_id", id);
    tbuilder.append("_tm", (unsigned int)time(0));
    for( BSONObj::iterator i = user.begin(); i.more(); ){
        BSONElement e = i.next();
        tbuilder.append(e);
    }
    BSONObj t=tbuilder.obj();
    m_db.insert(m_users, t);
    if(m_db.getLastError()!=""){
        throw WAException(WA_ERR_USER, m_db.getLastErrorDetailed().getIntField("code"));
    }
    return id;
}

BSONObj DBInterface::login(string user, string passwd){
    BSONObj obj = BSON("user"<<user<<"pass"<<passwd);
    BSONObj rst = m_db.findOne(m_users, obj);
    if(m_db.getLastError()!=""){
        throw WAException(WA_ERR_USER, m_db.getLastErrorDetailed().getIntField("code"));
    }
    if(rst.nFields()<1){
        throw WAException(WA_ERR_USER_PASSERR);
    }

    return rst;
}

/**
 * 更新一个user， 登录亦可用
 */
state_t DBInterface::updUser(string user, BSONObj obj){
    BSONObjBuilder      builder;
    set<string>         fields;

    obj.getFieldNames(fields);
    for(set<string>::iterator iter=fields.begin();
            iter!=fields.end(); iter++){
        builder.append("$set", BSON(*iter<<obj.getField(*iter)));
    }

    m_db.update(m_users, BSON("user"<<user), builder.obj());

    if(m_db.getLastError()!=""){
        throw WAException(WA_ERR_USER, m_db.getLastErrorDetailed().getIntField("code"));
    }

    if(obj.hasField("gps")){
        try{
            m_db.update(m_locations, BSON("user"<<user), BSON("$push"<<BSON("locations"<<BSON("_tm"<<(unsigned int)time(0)<<"gps"<<obj.getField("gps")))), true);
        }catch(DBException&e ){
        }
    }

    return BS_SUCCESS;
}

BSONObj DBInterface::getUser(string user){
    return m_db.findOne(m_users, BSON("user"<<user));
}
/**
 * 添加user的关注
 */
state_t DBInterface::addFollow(string user, string follow){
    m_db.update(m_follows, BSON("user"<<user), BSON("$addToSet"<<BSON("fol"<<follow)), true);
    if(m_db.getLastError()!=""){
        throw WAException(WA_ERR_USER, m_db.getLastErrorDetailed().getIntField("code"));
    }
    return addFan(follow, user);
}
/**
 * 获取user的关注列表
 */
BSONObj DBInterface::getFollows(string user, int start, int num){
    if(num==0){
        return m_db.findOne(m_follows, BSON("user"<<user))["fol"].Obj();
    }else{
        BSONObj obj = BSON("fol"<<1<<"_id"<<0<<"fol"<<BSON("$slice"<<BSON_ARRAY(start<<num)));
        return m_db.findOne(m_follows, BSON("user"<<user), &obj)["fol"].Obj();
    }
}
/**
 * 删除user的关注
 */
state_t DBInterface::delFollow(string user, string follow){
    m_db.update(m_follows, BSON("user"<<user), BSON("$pull"<<BSON("fol"<<follow)));
    if(m_db.getLastError()!=""){
        throw WAException(WA_ERR_USER, m_db.getLastErrorDetailed().getIntField("code"));
    }
    return BS_SUCCESS;
}
/**
 * 添加user的粉丝（一个人的好友关系为2个表，我的关注表，我的粉丝表即关注我的人）
 */
state_t DBInterface::addFan(string user, string fan){
    m_db.update(m_fans, BSON("user"<<user), BSON("$addToSet"<<BSON("fan"<<fan)), true);
    if(m_db.getLastError()!=""){
        throw WAException(WA_ERR_USER, m_db.getLastErrorDetailed().getIntField("code"));
    }
    return BS_SUCCESS;
}
/**
 * 获取关注user的列表
 */
BSONObj DBInterface::getFans(string user, int start, int num){
    if(num==0){
        BSONObj obj = m_db.findOne(m_fans, BSON("user"<<user));
        if(obj.nFields()>0){
            return obj["fan"].Obj();
        }
        else{
            throw WAException(WA_ERR_USER_NOTFOUND);
        }
    }else{
        BSONObj obj = BSON("fan"<<1<<"_id"<<0<<"fan"<<BSON("$slice"<<BSON_ARRAY(start<<num)));
        BSONObj fans = m_db.findOne(m_fans, BSON("user"<<user), &obj);
        if(obj.nFields()>0){
            return obj["fan"].Obj();
        }
        else{
            throw WAException(WA_ERR_USER_NOTFOUND);
        }
    }
}

long long DBInterface::addTopic(BSONObj topic){
    BSONObjBuilder tbuilder;
    unsigned int tm = time(0);
    
    long long id = getCounterId(m_topics);
    if(id < 0){
        throw string("Get Topic Id Error");
    }
    tbuilder.append("_id", id);
    tbuilder.append("_tm", tm);
    for( BSONObj::iterator i = topic.begin(); i.more(); ){
        BSONElement e = i.next();
        tbuilder.append(e);
    }
    BSONObj t=tbuilder.obj();
    m_db.insert(m_topics, t);

    // 修改最近地点
    try{
        string user = topic["user"].String();
        updUser(user, BSON("gpstm"<<tm<<"gps"<<topic.getField("gps")));

        m_db.update(m_locations, BSON("user"<<user), BSON("$push"<<BSON("locations"<<BSON("_tm"<<tm<<"gps"<<topic.getField("gps")))), true);
    }catch(DBException& e){
    }

    //添加到fans的inbox
    // 另外一个选择是发送到dbserver，异步执行，此处直接返回
    // dbserver处理对时间要求低的请求
    try{
        BSONObj fans = getFans(topic.getField("user").String());
        BSONObjIterator iter(fans);
        BSONForEach(iter, fans){
            m_db.update(m_inbox, BSON("user"<<iter.toString()), BSON("$push"<<BSON("in"<< id)), true);
        }
        if(m_db.getLastError()!=""){
            throw WAException(WA_ERR_USER, m_db.getLastErrorDetailed().getIntField("code"));
        }
    }catch(DBException& e){
        throw WAException(e.what());
    }
    catch(WAException& e){
    }

    return id;
}

/**
 * 获取user的发表
 */
BSONObj DBInterface::getTopics(string user, int start, int num){
    return m_db.query(m_topics, BSON("user"<<user));
}
/**
 * 获取一个发表
 */
BSONObj DBInterface::getTopic(long long topic){
    return m_db.findOne(m_topics, BSON("_id"<<topic));
}
/**
 * 获取评论
 */
BSONObj DBInterface::getPosts(int64_t topic){
    BSONObjBuilder      builder;
    BSONObj             obj;
    
    return obj;
}
/**
 * 获取user的位置列表
 */
BSONObj DBInterface::getLocations(string user, int start ,int num){
    BSONObjBuilder      builder;
    BSONObj             obj;
    
    if(num==0){
        return m_db.findOne(m_locations, BSON("user"<<user))["fol"].Obj();
    }else{
        BSONObj obj = BSON("fol"<<1<<"_id"<<0<<"fol"<<BSON("$slice"<<BSON_ARRAY(start<<num)));
        return m_db.findOne(m_locations, BSON("user"<<user), &obj)["fol"].Obj();
    }
    return obj;
}
/**
 * 获取附近user
 */
BSONObj DBInterface::getNearUsers(double x, double y, int start, int num){
    //BSONObj     obj = BSON("loc"<<1<<"_id"<<0);
    return m_db.query(m_users, BSON("gps"<<BSON("$near"<<BSON_ARRAY(x<<y))));
                
}
/**
 * 获取附近发表
 */
BSONObj DBInterface::getNearTopics(double x, double y, int start, int num){
    return m_db.query(m_topics, BSON("gps"<<BSON("$near"<<BSON_ARRAY(x<<y))));
}

int64_t DBInterface::getCounterId(string collection){
    BSONObj             robj;
    BSONObjBuilder      builder;
    int64_t             id = 0;

    builder.append("findAndModify", DB_COUNTER);
    builder.append("query", BSON("_id"<<collection));
    builder.append("update", BSON("$inc"<<BSON("counter"<<(long long)1)));
    builder.append("new", true);
    builder.append("upsert", true);
    BSONObj obj = builder.obj();
    m_db.runCommand(m_project, obj, robj);
    if(m_db.getLastError()!=""){
        throw WAException(WA_ERR_DEF, m_db.getLastErrorDetailed().getIntField("code"));
    }

    if(robj.getField("value").isNull()){
        return -1;
    }
    if(robj["value"]["counter"].type()==NumberInt){
        id = robj["value"]["counter"].Int();
    }
    else{
        id = robj["value"]["counter"].Long();
    }

    return id;
}
